#include "lib.h"

#pragma warning( disable: 4786 )
#include <vector>

#include "slotconn.h"
#include "sltsql.h"
#include "slotguts.h"
#include "getrand.h"
#include "opts.h"
#include "log.h"

#include "RoninGuts.h"				//  1 Ronin
#include "cleosgoldguts.h"			//  2 Cleopatra's Gold
#include "AztecsTreasureGuts.h"		//  3 Aztec's Treasure
#include "goldbeardguts.h"			//  4 Goldbeard
#include "CaesarsEmpireGuts.h"		//  5 Caesar's Empire
#include "diamonddozenguts.h"		//  6 Diamond Dozen
#include "PrinceOfSherwoodGuts.h"   //  7 Prince of Sherwood
#include "VikingsVoyageGuts.h"		//  8 Viking's Voyage
#include "LionsLairGuts.h"			//  9 Lion's Lair
#include "MisterMoneyGuts.h"		// 10 Mister Money
#include "DerbyDollarsGuts.h"		// 11 Derby Dollars
#include "TigerTreasuresGuts.h"		// 12 Tiger Treasures
#include "SunkenTreasureGuts.h"		// 13 Sunken Treasure
#include "MermaidQueenGuts.h"		// 14 Mermaid Queen
#include "HillbilliesGuts.h"		// 15 Hillbillies
#include "FruitFrenzyGuts.h"		// 16 Fruit Frenzy
#include "TallyHoGuts.h"			// 17 Tally Ho
#include "RainDanceGuts.h"			// 18 Rain Dance
#include "GreenLightGuts.h"			// 19 Green Light
#include "RedSandsGuts.h"			// 20 Red Sands
#include "TripleToucanGuts.h"	    // 21 Triple Toucan
#include "FunkyMonkeyGuts.h"	    // 22 Funky Monkey
#include "EnchantedGardenGuts.h"	// 23 Enchanted Garden
#include "VictoryGuts.h"			// 24 Victory
#include "AladdinsWishesGuts.h"		// 25 Aladdin's Wishes
#include "CrystalWatersGuts.h"		// 26 Crystal Waters
#include "GoldenGloveGuts.h"		// 27 Golden Glove
#include "IncanGoddessGuts.h"		// 29 Incan Goddess
#include "OuttaThisWorldGuts.h"		// 30 Outta This World
#include "ParadiseDreamsGuts.h"     // 31 Paradise Dreams
#include "AchillesGuts.h"			// 32 Achilles
#include "HoneyToTheBeeGuts.h"		// 33 Honey to the Bee
#include "RealmOfRichesGuts.h"      // 35 Realm Of Riches
#include "PenguinPowerGuts.h"		// 36 Penguin Power
#include "TreasureChamberGuts.h"	// 37 Treasure Chamber
#include "ParisBeautyGuts.h"		// 38 Paris Beauty
#include "FameAndFortuneGuts.h"     // 39 Fame and Fortune
#include "BigShotGuts.h"			// 40 Big Shot
#include "JackpotPinatasGuts.h"     // 41 Jackpot Pinatas
#include "FieldOfGreenGuts.h"		// 200 Field of Green
#include "WitchesAndWizardsGuts.h"	// Witches And Wizards (Genesis slot)
#include "HydroHeatGuts.h"			// Hydro Heat (Genesis slot)
#include "ReelBloodGuts.h"			// Reel Blood (Genesis slot)
#include "ElLuchadorGuts.h"			// El Luchador (Genesis slot)
#include "TeaCupDragonGuts.h"		// Tea Cup Dragons (Genesis slot)
#include "IndianaJoanGuts.h"		// Indiana Jane (Genesis slot)
#include "CashMoneyMermaidsGuts.h"	// Cash Money Mermaids (Genesis slot)
#include "ZombiesVsCheerleadGuts.h"	// Zombies Vs Cheerleaders (Genesis slot)
#include "FootballFrenzyGuts.h"		// Football Frenzy (Genesis slot)
#include "ZombiesVsCheerlead2Guts.h"// Zombies Vs Cheerleaders 2 (Genesis slot)
#include "SaturnGPGuts.h"			// 161 (0XA1) Saturn GP (Korean Team Slot)



SlotGuts *
SlotGuts::createGuts(uint8 machid)
{
    SlotGuts *guts = NULL;

    switch ( machid )
    {
		case MACHID_RONIN:
            guts = new RoninGuts();
            break;

		case MACHID_CLEOSGOLD:
			guts = new CleosGoldGuts();
			break;

		case MACHID_AZTECSTREASURE:
			guts = new AztecsTreasureGuts();
			break;

		case MACHID_GOLDBEARD:
			guts = new GoldbeardGuts();
			break;

    	case MACHID_CAESARSEMPIRE:
    		guts = new CaesarsEmpireGuts();
    		break;

		case MACHID_DIAMONDDOZEN:
			guts = new DiamondDozenGuts();
			break;

		case MACHID_PRINCEOFSHERWOOD:
			guts = new PrinceOfSherwoodGuts();
			break;

		case MACHID_VIKINGSVOYAGE:
			guts = new VikingsVoyageGuts();
			break;

		case MACHID_LIONSLAIR:
			guts = new LionsLairGuts();
			break;

		case MACHID_MISTERMONEY:
			guts = new MisterMoneyGuts();
			break;

		case MACHID_DERBYDOLLARS:
			guts = new DerbyDollarsGuts();
			break;

		case MACHID_TIGERTREASURES:
			guts = new TigerTreasuresGuts();
			break;

		case MACHID_SUNKENTREASURE:
			guts = new SunkenTreasureGuts();
			break;

		case MACHID_MERMAIDQUEEN:
			guts = new MermaidQueenGuts();
			break;

		case MACHID_HILLBILLIES:
			guts = new HillbilliesGuts();
			break;

		case MACHID_FRUITFRENZY:
			guts = new FruitFrenzyGuts();
			break;

		case MACHID_TALLYHO:
			guts = new TallyHoGuts();
			break;

		case MACHID_RAINDANCE:
			guts = new RainDanceGuts();
			break;

		case MACHID_GREENLIGHT:
			guts = new GreenLightGuts();
			break;

		case MACHID_REDSANDS:
			guts = new RedSandsGuts();
			break;

		case MACHID_TRIPLETOUCAN:
			guts = new TripleToucanGuts();
			break;

		case MACHID_FUNKYMONKEY:
			guts = new FunkyMonkeyGuts();
			break;

		case MACHID_ENCHANTEDGARDEN:
			guts = new EnchantedGardenGuts();
			break;

		case MACHID_VICTORY:
			guts = new VictoryGuts();
			break;

		case MACHID_ALADDINSWISHES:
			guts = new AladdinsWishesGuts();
			break;

		case MACHID_CRYSTALWATERS:
			guts = new CrystalWatersGuts();
			break;

		case MACHID_GOLDENGLOVE:
			guts = new GoldenGloveGuts();
			break;

		case MACHID_INCANGODDESS:
			guts = new IncanGoddessGuts();
			break;

		case MACHID_OUTTATHISWORLD:
			guts = new OuttaThisWorldGuts();
			break;

		case MACHID_PARADISEDREAMS:
		case MACHID_TREASUREISLAND:	// 0xA0	Treasure Island (Paradise Dreams Clone) CSN-5671
			guts = new ParadiseDreamsGuts();
			break;

		case MACHID_ACHILLES:
			guts = new AchillesGuts();
			break;

		case MACHID_HONEYTOTHEBEE:
			guts = new HoneyToTheBeeGuts();
			break;

		case MACHID_REALMOFRICHES:
			guts = new RealmOfRichesGuts();
			break;

		case MACHID_PENGUINPOWER:
			guts = new PenguinPowerGuts();
			break;

		case MACHID_TREASURECHAMBER:
			guts = new TreasureChamberGuts();
			break;

		case MACHID_PARISBEAUTY:
			guts = new ParisBeautyGuts();
			break;

		case MACHID_FAMEANDFORTUNE:
			guts = new FameAndFortuneGuts();
			break;
			
		case MACHID_BIGSHOT:
			guts = new BigShotGuts();
			break;

        case MACHID_JACKPOTPINATAS:
            guts = new JackpotPinatasGuts();
            break;

		case MACHID_DIRTYMARTINI: // [AO 2007-11-28] Dirty Martini Slots
			guts = new DirtyMartiniGuts();
			break;

		case MACHID_WITCHESANDWIZARDS: // 0x60 [CGC 2010-03-16] Witches And Wizards (First Genesis Slot)
		case MACHID_DRAGONS:	// 0x66	[CGC 2012-01-19] Dragons (Witches And Wizards Clone) CSN-5049
		case MACHID_HERCULES:	// 0x68	[CGC 2012-01-19] Hercules (Witches And Wizards Clone) CSN-5260
		case MACHID_QUEEONOFKINGS: // 0X6F Queen of Kings (Witches And Wizards Clone) 
			guts = new WitchesAndWizardsGuts();
			break;

		case MACHID_HYDROHEAT: // 0x61 [CGC 2010-07-30] Hydro Heat (Genesis Slot)
			guts = new HydroHeatGuts();
			break;

		case MACHID_REELBLOOD: // 0x62 [CGC 2010-08-18] Reel Blood (Genesis Slot)
		case MACHID_ARES: 	// 0x67	[CGC 2012-01-19] Ares (Reel Blood Clone)  CSN-5055
		case MACHID_INVASION:	// 0x69	[CGC 2012-01-19] Invasion (Reel Blood Clone) CSN-5263
		case MACHID_GLADIATORWARS:	// 0x72	Gladiator Wars (Reel Blood Clone) CSN-6779
		case MACHID_SIXSHOTSHOWDOWN:	// 0x73	Six Shot Showdown (Reel Blood Clone) CSN-6704
			guts = new ReelBloodGuts();
			break;

		case MACHID_ELLUCHADOR:				// 0x63 [CGC 2011-07-29] El Luchador (Genesis Slot)
		case MACHID_BIRDSOFFURY:			// Birds of Fury (CSN-6733)
		case MACHID_DRAGONPRINCESS:			// Dragon Princess (CSN-6879)
		case MACHID_SNOWWHITE:				// Snow White (CSN-6874)
			guts = new ElLuchadorGuts();
			break;

		case MACHID_TEACUPDRAGON: // 0x64 [CGC 2011-05-27] Tea Cup Dragons (Genesis Slot)
			guts = new TeaCupDragonGuts();
			break;

		case MACHID_INDIANAJOAN: // 0x65 Indiana Joan (Genesis Slot)
		case MACHID_DJMOOCOW:	// 0x6C DJ Moo Cow (Genesis Slot)
		case MACHID_DESERTKINGDOM:	// 0x6D	Desert Kingdom (Genesis Slot) CSN-6565
		case MACHID_DINOISLAND: //0X71 Dino Island (Genesis Slot) CSN-6731
			guts = new IndianaJoanGuts();
			break;

		case MACHID_CASHMONEYMERMAIDS:	// 0x6A Cash Money Mermaids (Genesis Slot)
		case MACHID_JACKTHERIPPER:		// 0x6B Jack The Ripper (Genesis Slot)
		case MACHID_BIGMONEYBIGFOOT:	// 0x6E Big Money Big Foot (Genesis Slot)	
			guts = new CashMoneyMermaidsGuts();
			break;

		case MACHID_ZOMBIES_CHEERLEADERS:
			guts = new ZombiesVsCheerleadGuts();
			break;

		case MACHID_FOOTBALL_FRENZY:
			guts = new FootballFrenzyGuts();
			break;

		case MACHID_ZOMBIES_CHEERLEADERS2:
			guts = new ZombiesVsCheerlead2Guts();
			break;

		case MACHID_SATURNGP: // 0xA1 Saturn GP (Korean Team Slot)
			guts = new SaturnGPGuts();
			break;
			
		case MACHID_FIELDOFGREEN:
			guts = new FieldOfGreenGuts();
			break;

        default:			
			ASSERT(false);
			break;

    }

    if ( guts )
    {
        guts->m_machid = machid;
    }

    return guts;
}

SlotGuts::SlotGuts()
{
    m_hardMalfunction = false;
	m_info = (Info *) 0;
	setGameCanComplete(false);
	m_hasSavedGame = false;
	m_subGame = 0;
	m_feature = 0;
    m_bCanResetForNewGame = true;
    m_payTable = new ISlotPayColumn();
    m_spin = NULL;

	m_bDebugSpinsMode = false;

#pragma revisit("Test progressive bet spin testing logic")
	// NOTE: Since Bodog does not have cross-casino progressive, this is always false
	m_bSpinTestAllowProgressiveBet = false;

#ifdef DEBUGSPIN
	m_bDebugSpinsMode = ServerOpts::bAllowTesting && ServerOpts::IsDebugCasinoName();
#endif

#ifdef ALLOW_SPIN_TESTING
	m_bSpinTestAllowProgressiveBet = ServerOpts::bAllowTesting && ServerOpts::IsDebugCasinoName();
#endif

	if(m_bDebugSpinsMode)
	{
		Log::msgInformation("Debug Spins Mode is active");
	}

	m_debugSpinCount = 0;
	m_bForceRandomJackpot = false;
}

SlotGuts::~SlotGuts()
{
    delete m_subGame;
	delete m_feature;
    delete m_spin;
    clearWheelhouse();
    clearPayTable();
    clearPaylines();
    clearDenominations();
	clearProbabilityTables();

	for(DebugFunctionMap::iterator it = m_mapDebugFunctions.begin(), end = m_mapDebugFunctions.end();
		 it != end; ++it)
	{
		delete (it->second);
	}
}

void SlotGuts::clearProbabilityTables()
{
    while ( m_probabilityTables.size() > 0 )
    {
        delete m_probabilityTables.back();
        m_probabilityTables.pop_back();
    }
}

void SlotGuts::addProbabilityTable( int32 nTableID )
{
	SlotProbabilityTable* pTable = new SlotProbabilityTable( nTableID );

	m_probabilityTables.push_back( pTable );
}

SlotProbabilityTable* SlotGuts::getProbabilityTable( int32 nTableID ) const
{
    for ( SlotProbabilityTableVector::const_iterator it = m_probabilityTables.begin(); it != m_probabilityTables.end(); it++ )
	{
		if ( (*it)->getTableID() == nTableID )
		{
			return *it;
		}
	}

	ASSERT( false );
	return NULL;
}

void SlotGuts::addProbabilityTableEntry( int32 nTableID, uint32 nMaxWeightForValue, int32 nValue )
{
	SlotProbabilityTable* pTable = getProbabilityTable( nTableID );

	if ( pTable )
	{
		pTable->addEntry( nMaxWeightForValue, nValue );
	}
}

int32 SlotGuts::getRandomValueFromProbabilityTable( int32 nTableID /*= 0*/ ) const
{
	SlotProbabilityTable* pTable = getProbabilityTable( nTableID );
	int32 nReturn = 0;

	if ( pTable )
	{
		nReturn = pTable->getRandomValue();
	}

	return nReturn;
}

void
SlotGuts::clearPaylines()
{
    while ( m_paylines.size() > 0 )
    {
        SlotPaylineReel *paylinereel = m_paylines.back();

        while ( paylinereel->size() > 0 )
        {
            SlotPaylinePosition *position = paylinereel->back();
            delete position;
            paylinereel->pop_back();
        }
        delete paylinereel;
        m_paylines.pop_back();
    }
}
		
void *
SlotGuts::getCustomData()
{
    ASSERT(0);
    return (void *) 0;
}

void
SlotGuts::resetForNewGame()
{
	setGameCanComplete(false);
    delete m_spin;
    m_spin = NULL;
}

int64
SlotGuts::getReferenceGameNum()
{
    return m_subGame->getData(SGIDX_REFERENCE_GAME_NUM);
}

void
SlotGuts::setReferenceGameNum(int64 nGameNum)
{
    m_subGame->setServerOnly(SGIDX_REFERENCE_GAME_NUM,true);
    m_subGame->update(SGIDX_REFERENCE_GAME_NUM,nGameNum);
}

int32
SlotGuts::getJackpotAmount()
{
    return m_subGame->getData(SGIDX_JACKPOT_AMOUNT);
}

void
SlotGuts::setJackpotAmount(int32 nJackpotAmount)
{
    m_subGame->update(SGIDX_JACKPOT_AMOUNT,nJackpotAmount);
}

int32
SlotGuts::getGutsId()
{
    return m_gutsid;
}

void
SlotGuts::getGutsName(char* name)
{
   SlotsSQL *slotTracker = (SlotsSQL *) tracker;

    if ( tracker )
    {
    	slotTracker->lock();
        slotTracker->getGutsName(m_gutsid,name);
    	slotTracker->unlock();
    }
}

void
SlotGuts::getLocale(localCurrency* pLocalCurrency)
{
    SlotsSQL *slotTracker = (SlotsSQL *) tracker;
    if ( tracker )
    {
        slotTracker->lock();
        slotTracker->GetLocale(pLocalCurrency);
        slotTracker->unlock();
    }
}

void
SlotGuts::setGutsId(int32 gutsId)
{
    m_gutsid = gutsId;

	// CGC 20100608. To indicate it is a new game which works through the new casino API
	isThirdParty = gutsId >= 9000 && gutsId < 10000;

#ifdef DEBUGSPIN
	if(m_bDebugSpinsMode && shouldSaveGameData())
	{
		m_nDebugSpinIndex = 0;

		SlotsSQL *slotTracker = (SlotsSQL *) tracker;
		if ( tracker )
		{
			slotTracker->lock();
			slotTracker->getDebugSpins(m_gutsid, m_debugReelStops, m_debugSpinCount,getPid());
			slotTracker->unlock();
		}
	}
#endif
}

		
const PID&
SlotGuts::getPid()
{
    return m_info->pid;
}



bool8
SlotGuts::isForMoney()
{
    return m_info->forMoney;
}

bool8
SlotGuts::shouldSaveGameData()
{
	if ( isForMoney() )
	{
		return true;
	}

	if ( ServerOpts::bSaveFunData )
	{
		return true;
	}

	return false;
}


int32
SlotGuts::getJackpotId()
{
    return m_jackpotid;
}

void
SlotGuts::setJackpotId(int32 jackpotId)
{
    m_jackpotid = jackpotId;
}

void
SlotGuts::setJackpotHitOdds(int32 jackpotHitOdds)
{
    m_jackpotHitOdds = jackpotHitOdds;
}

bool8
SlotGuts::isProgressive()
{
    return m_progressive;
}

void
SlotGuts::setProgressive(bool8 progressive)
{
    m_progressive = progressive;
}

bool8
SlotGuts::getProgressiveContribution(int32 nBetPerLine, int32 nLineCount, int32& nContribution, int32& nContributionFractionalCents)
{
	// Return false by default to just allow the stored procedures to do the math using
	// the bet amount and the contribution percentage from the database.
    return false;
}

uint8
SlotGuts::getMachId()
{
    return m_machid;
}



void
SlotGuts::loadSubGameData()
{
    // kharmon.  04-05-07.  #19309.  Don't load subgame data if the player is playing for fun and fundata isn't being saved.
	if ( shouldSaveGameData() )
	{
    SlotsSQL *slotTracker = (SlotsSQL *) tracker;

    if ( tracker )
    {
    	slotTracker->lock();
        slotTracker->getSubGameData(m_subGame);
    	slotTracker->unlock();
    }
	getFeature()->setGameNum(getReferenceGameNum());
	loadFeatureData();
    }
}


void
SlotGuts::saveSubGameData()
{
	if ( shouldSaveGameData() )
	{
		if ( getSubGameSize() > 0 )
		{
			SlotsSQL *slotTracker = (SlotsSQL *) tracker;

			if ( tracker )
			{
    			slotTracker->lock();
				slotTracker->setSubGameData(m_subGame);
    			slotTracker->unlock();
			}
		}
		saveFeatureData();
	}
}

// Call this method after game is saved, otherwise you cannot get a valid gamenum
// SlotGuts::saveGame() should have been called before you call this method.
/*
void
SlotGuts::archiveSubGameData(int64 gamenum)
{
    if ( getSubGameSize() > 0 && gamenum > 0)
    {
        SlotsSQL *slotTracker = (SlotsSQL *) tracker;

        if ( tracker )
        {
    		slotTracker->lock();
            slotTracker->archiveSubGameData(gamenum, m_subGame);
    		slotTracker->unlock();
        }
    }
}
*/

void
SlotGuts::allocateSubGameData()
{
    m_subGame = new SubGame(m_info->machineID, m_info->pid, m_info->machineSubID, m_info->forMoney);
}

int32
SlotGuts::getSubGameSize()
{
    return m_subGame->getDataCount();
}


SubGame *
SlotGuts::getSubGame()
{
    return m_subGame;
}



void
SlotGuts::loadFeatureData()
{
    SlotsSQL *slotTracker = (SlotsSQL *) tracker;

    if ( tracker )
    {
    	slotTracker->lock();
        slotTracker->getFeatureData(m_feature);
    	slotTracker->unlock();
    }

}


void
SlotGuts::saveFeatureData()
{
	if ( shouldSaveGameData() )
	{
		if ( getFeatureSize() > 0 )
		{
			SlotsSQL *slotTracker = (SlotsSQL *) tracker;

			if ( tracker )
			{
    			slotTracker->lock();
				slotTracker->setFeatureData(m_feature);
    			slotTracker->unlock();
			}
		}
	}
}

void
SlotGuts::allocateFeatureData()
{
    m_feature = new Feature();
}

int32
SlotGuts::getFeatureSize()
{
    return m_feature->getDataCount();
}


Feature *
SlotGuts::getFeature()
{
    return m_feature;
}


SlotSpin *
SlotGuts::getSlotSpin()
{
    return m_spin;
}

bool8
SlotGuts::processClientData(uint8 dataType, int32 dataCount, int32 *data)
{
    /* empty for subclass behavior */
	return false;
}

// CSN-5756: with the new korean team game (Saturn GP) we need to support fixed jackpot amounts
//           Only games with a fixed jackpot instead of a progressive one will have to implement this
int32 
SlotGuts::getFixedJackpotValue()
{
	return 0;
}

SlotSpin *
SlotGuts::spin(uint8 bet, int32 multibankBetCents, uint8 lineCount, uint8 bonusGame, bool8 bAutoplay)
{
    SlotSpin *pSpin = new SlotSpin();

    if ( getFreeSpins() > 0 )
    {
        pSpin->setBet(0);
        pSpin->setMultibankBet(0);
        m_info->betperline = getBetPerLine();
    }
    else
    {
        pSpin->setBet(1);
        pSpin->setMultibankBet(multibankBetCents);
        setTotalWin(0);
		setMaxPayoutAwarded( false );
        setGameModeId(0);
        setBetPerLine(m_info->betperline);
        setNumLinesBet(lineCount);
        setReferenceGameNum(0);
		setJackpotAmount(0);
    }

    for ( uint8 i = 0; i < getNumLinesBet(); i++ )
    {
        pSpin->addLine(i+1);
    }

    pSpin->setBonusGameType(bonusGame);
    pSpin->setAutoplay(bAutoplay);

	pSpin->setNumRows(getRowCount());

    spin(pSpin);

    return pSpin;
}
		
SlotSpin *
SlotGuts::spin(int32 multibankBetCents, uint8 lineCount, uint8 bonusGame, bool8 bAutoplay)
{
    return spin(getFreeSpins() > 0 ? 0 : 1, multibankBetCents, lineCount, bonusGame, bAutoplay);
}


void
SlotGuts::spin(SlotSpin *spin)
{
	uint8 *stops = new uint8[getReelCount()];
    uint8 i;
	
    m_hardMalfunction = false;

	// set the total bet of the inital spin.
	// this will be used to see if the initial spin and the Bonus Spins qualify for the
	// jackpot in the getRandomProgressiveJackpotResult() function

    spin->setGameModeId(getGameModeId());
	
	resetDataForNextSpin();
	
	for ( i = 0; i < getReelCount(); i++ )
	{
        uint32 nRand;

		do
		{
            nRand = getrand();
		} while ( nRand > getReelMaxWeight( getGameModeId(), i ) );

		stops[i] = findPositionByWeight(getGameModeId(), nRand, i);
	}

	spin->setStops(getReelCount(), stops);

	// After the reels have stopped, before they are evaluated,
	// allow the derived slot machine to take some action.
	preSpinClassifyResults( spin );

    setGameCanComplete(true);

	classifyResult( spin );
	
	spin->setSpinComplete(true);

	m_spin = spin;

    if ( getFreeSpins() > 0 && spin->getBet() == 0 )
    {
		decrementFreeSpins();		//decrement the free spins counter
    }
	
	// This needs to happen outside the previous if,
	// because the free spin count may already be zero (in the case of may payout),
	// and the game mode not yet changed.
	if ( getFreeSpins() == 0 )
	{
		setGameModeId(0);
	}

	// After the reels have been evaluated, again allow
	// the derived slot machine to take some action.
	postSpinClassifyResults( spin );

    spin->setNumSpinsRemaining(getFreeSpins());

    delete [] stops;
}



uint8
SlotGuts::relativeWheelPosition(uint8 gamemodeid, uint8 reelid, uint8 position, uint8 offset) const
{
    uint8 newPosition = position + offset;

    if ( newPosition >= getIconCount(gamemodeid, reelid) )
    {
        newPosition -= this->getIconCount(gamemodeid, reelid);
    }

    return newPosition;
}

void
SlotGuts::classifyResult(SlotSpin *spin)
{
	uint8 *classifyPositions = new uint8[spin->getStopCount()];
    uint8 bet;
    uint8 result = 0;
	
    /**
     * Go through the played paylines one at a time looking for winners.
     */
    for ( uint8 lineidx = 0; lineidx < spin->getSpinLineCount(); lineidx++ )
    {
        SpinLine *spinLine = spin->getSpinLineAt(lineidx);

        /**
         * Iterate the stops to get the classify positions.
         */
        for ( uint8 stopid = 0; stopid < spin->getStopCount(); stopid++ )
        {
            uint8 offsetPosition = getPayline(spinLine->lineid, stopid)->wheelposition;
            /**
             * Calculate the offset so we are using the proper icon
             * for this payline.
             */
            classifyPositions[stopid] = relativeWheelPosition(getGameModeId(), stopid, spin->getStops()[stopid], offsetPosition);
        }

        uint8 multiplier = 1;

        classifyStops(spinLine, classifyPositions,spin->getBet(), lineidx, result, multiplier);

        /**
         * If the bet is 0 we assume this is some special case and
         * use max-bet instead.  Prior bet validation prevents a spin from
         * happeneing with a 0 bet that isn't supposed to happen.
         */
        bet = spin->getBet();

        /**
         * Update the payout for this line.  Note we have to multiply the payout
         * by the denomination of the machine.
         */
		
		// find the number of lines times the betperline.  If it is larger than $1 (100 cents)
		//	then
		
        uint8 nGameModeMultiplier = 1;
        uint8 nNoteId = 0;

        getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );

        if ( nGameModeMultiplier > 1 )
        {
            multiplier *= nGameModeMultiplier;
            spinLine->addNote( nNoteId );
        }

        int32 nPayout = getPayout(result)->payoutAmount*multiplier;

        // CGC 20100608. If it is a third party game (Genesis) we consider the bet per line in the total amount win
		if (!isThirdParty)
		{
			if ( ( getTotalWin() + nPayout ) >= getFeatureGameCap() )
			{
				nPayout = getFeatureGameCap() - getTotalWin();

				// Set the max payout flag.
				setMaxPayoutAwarded();

				// Get rid of the remaining free spins.
				removeAllFreeSpins();
			}
		}
		else
		{
			nPayout = nPayout * getBetPerLine();
		}
		
        setTotalWin( getTotalWin() + nPayout );

		spin->setPayoutAmount(spinLine->lineid,		                           // Line ID
					getPayout(result)->payoutAmount * getBetPerLine(),         // Base Payout Amount
                    multiplier,                                                // Need to calc multiplier
					isThirdParty ? nPayout : nPayout * getBetPerLine(),        // Actual payout (capped if necessary) //CGC: if thirdparty game, getperline is already included in nPayout
                    false,	                                                   // Jackpot
					result);	                                               // Payouttypeid
	}

	// Classify any scatter pays (implemented as a null function in the base)
	spin->clearScatters();
	classifyScatters(spin);

    // Generate the wheel view for reporting
    storeWheelView(spin);

	// Now see if the scatters put us over the maximum win.  If it is equal to the maximum
    // win we still need to check to get rid of any additional free spin scatters.
	// CGC 20100608. If it is a third party game (Genesis) we don't consider this
	if (!isThirdParty)
	{
		if ( getTotalWin() >= getFeatureGameCap() )
		{
			// Set the max payout flag.
			setMaxPayoutAwarded();

			// Get rid of the remaining free spins.
			removeAllFreeSpins();

			// See how much we have to reduce the total win by.
			long nReduceBy = getTotalWin() - getFeatureGameCap();
		
			// Loop through all of the scatters in reverse order.
			for ( int8 n = spin->getNumScatters() - 1; n >= 0; n-- )
			{
				// Get the scatter for the current iteration.
				SlotScatter* pScatter = spin->getScatter( n );

				// Calculate the scatter pay ratio.  The total win and the feature game cap are
				// expressed in terms of the pay ratio, but the scatter payout amount is in currency.
				// The equivelent pay ratio for the scatter can be calculated by dividing it by the
				// bet per line, since the feature game cap is expressed as so many times the bet per
				// line.
				long nTotalScatterPayRatio = pScatter->totalscatterpayoutamount / getBetPerLine();

				// If this scatter is enough to get us down to the feature game cap...
				if ( nTotalScatterPayRatio >= nReduceBy )
				{
					// then adjust the scatter payout amount by the amount we still need to reduce the
					// total win by.  Also we need to multiply by the bet per line to translate
					// nReduceBy back into currency instead of a pay ratio.
					pScatter->totalscatterpayoutamount -= nReduceBy * getBetPerLine();

					// Reduce the total win by the same amount we just reduced the scatter by, except
					// in this case the total win is expressed in terms of the pay ratio so we don't
					// need to translate it to a currency amount.
					setTotalWin( getTotalWin() - nReduceBy );

					// We don't have to reduce the winnings any more, but keep going through the loop
					// in case there are more scatters with free spins.
					nReduceBy = 0;
				}
				else
				{
					// Otherwise we need to take away all of the winnings from this scatter.
					pScatter->totalscatterpayoutamount = 0;

					// Reduce the total win by the pay ratio of the scatter winnings we just removed.
					setTotalWin( getTotalWin() - nTotalScatterPayRatio );

					// We don't have to reduce the next scatter as much now since we already took
					// some of the overage out.
					nReduceBy -= nTotalScatterPayRatio;
				}

				// This scatter can't give us any more free spins since we are already over the cap.
				pScatter->freespins = 0;
			}

			// We should always have exactly the game cap by the time we finish looping through all of
			// the scatters.
			ASSERT( getTotalWin() == getFeatureGameCap() );
		}
	}

	// We should never have a total win that is more than the game cap.
	if (!isThirdParty)
		ASSERT( getTotalWin() <= getFeatureGameCap() );

    delete [] classifyPositions;
}


/**
 * The base class classify assumes the following:
 *
 *   1. All reels have to match the same icon to win.
 *   2. The icon id and the payout id match.
 *
 * 7/10/03 - UPDATE JOSH- BECAUSE OF FEAR OF RUNNING ON THIS GUT
 * I'M MAKING IT SET EVERYTHING TO LOSER, AND RAISING AN ERROR.
 */

void
SlotGuts::classifyStops(SpinLine* spinline, uint8 *stops, uint8 bet, uint8 line, uint8& rtnresult, uint8& multiplier)
{
	rtnresult = 0;
	Log::msgError("IMPORTANT ERROR - CONTACT Bodog: Attempting to play a game that the server exe doesnt know about - machine ID: %d", m_gutsid);
}


uint8
SlotGuts::getReelCount(uint8 nGameModeId /* nGameModeId == 0 */ ) const
{
    uint8 nReturn = 0;

    if ( nGameModeId < m_vecWheelHouse.size() )
    {
        nReturn = m_vecWheelHouse[nGameModeId]->size();
    }

    return nReturn;
}


uint8
SlotGuts::getIconCount(uint8 nGameModeId, uint8 reelid) const
{
    uint8 nReturn = 0;

    if ( nGameModeId < m_vecWheelHouse.size() && reelid < m_vecWheelHouse[nGameModeId]->size() )
    {
        SlotWheelHouse* pWheelHouse = m_vecWheelHouse[nGameModeId];
        ISlotReel *reel = (*pWheelHouse)[reelid];
        nReturn = reel->size();
    }

    return nReturn;
}

uint8
SlotGuts::getPayoutCount() const
{
    return m_payTable->size();
}


const SlotGuts::SlotReel *
SlotGuts::getSlotReel(uint8 nGameModeId, uint8 reelid)
{
    ISlotReel *reel = NULL;

    if ( nGameModeId < m_vecWheelHouse.size() && reelid < m_vecWheelHouse[nGameModeId]->size() )
    {
        SlotWheelHouse* pWheelHouse = m_vecWheelHouse[nGameModeId];
        reel = (*pWheelHouse)[reelid];
    }

    return (SlotReel *) reel;
}


const SlotPayout *
SlotGuts::getPayout(uint8 payouttypeid) const
{

    if ( payouttypeid > m_payTable->size() )
    {
        /**
         * Payout id is too large.
         */
        ASSERT(0);
    }

    const SlotPayout *payout = (*m_payTable)[payouttypeid];

    return payout;
}

const SlotIcon *
SlotGuts::getIcon(uint8 nGameModeId, uint8 reelid, uint8 position) const
{
    SlotIcon* pIcon = NULL;

    if ( nGameModeId < m_vecWheelHouse.size() && reelid < m_vecWheelHouse[nGameModeId]->size() )
    {
        SlotWheelHouse* pWheelHouse = m_vecWheelHouse[nGameModeId];
        ISlotReel *reel = (*pWheelHouse)[reelid];

        if ( position < reel->size() )
        {
            pIcon = (*reel)[position];
        }
    }

    return pIcon;
}


void
SlotGuts::addIcon(uint8 gamemodeid, uint8 reelid, uint8 iconid, uint8 reelposition, int64 weight, const char *iconName)
{
    SlotIcon *icon;
    ISlotReel *reel;
    SlotWheelHouse* pWheelHouse = NULL;

    icon = new SlotIcon();

    icon->reelid = reelid;
    icon->iconid = iconid;
    icon->position = reelposition;
    icon->weight = weight;
    icon->iconName = new char[strlen(iconName) + 1];
    strcpy(icon->iconName, iconName);

    while ( gamemodeid >= m_vecWheelHouse.size() )
    {
        pWheelHouse = new SlotWheelHouse();
        m_vecWheelHouse.push_back(pWheelHouse);
    }

    pWheelHouse = m_vecWheelHouse[gamemodeid];

    while ( reelid >= pWheelHouse->size() )
    {
        reel = new ISlotReel();
        pWheelHouse->push_back(reel);
    }

	reel = (*pWheelHouse)[reelid];
    reel->push_back(icon);
}


void
SlotGuts::clearWheelhouse()
{
    while ( m_vecWheelHouse.size() > 0 )
    {
        SlotWheelHouse* pWheelHouse = m_vecWheelHouse.back();

        while ( pWheelHouse->size() > 0 )
        {
            ISlotReel *reel = pWheelHouse->back();
            while ( reel->size() > 0 )
            {
                SlotIcon *del = reel->back();
                delete [] del->iconName;
                delete del;
                reel->pop_back();
            }
            delete reel;
            pWheelHouse->pop_back();
        }

        delete pWheelHouse;
        m_vecWheelHouse.pop_back();
    }
}


const SlotGuts::SlotPayColumn *
SlotGuts::getPayColumn()
{
    return (SlotPayColumn *)m_payTable;
}


const SlotPayout *
SlotGuts::getMaxPayout()
{
    const SlotPayout *payout = m_payTable->back();

    return payout;
}

uint8
SlotGuts::getPaylineCount()
{
    SlotPaylineReel *paylinereel = m_paylines[0];

    return paylinereel->size();
}

uint8
SlotGuts::getPaylinePosition(uint8 paylineid, uint8 reelid)
{
    uint8 position = 255;

    SlotPaylinePosition *pPosition = getPayline( paylineid, reelid);

    if ( pPosition )
    {
        position = pPosition->wheelposition;
    }

    return position;
}

SlotPaylinePosition *
SlotGuts::getPayline(uint8 paylineid, uint8 reelid)
{
    SlotPaylinePosition *position = (SlotPaylinePosition *) 0;

    if ( reelid < m_paylines.size() )
    {
        SlotPaylineReel *paylinereel = m_paylines[reelid];

        if ( paylineid <= paylinereel->size() )
        {
            /**
             * Payline id is 1 based.
             */
            position = (*paylinereel)[paylineid - 1];
        }
        else
        {
            /**
             * paylineid is invalid.
             */
            ASSERT(0);
        }
    }
    else
    {
        /**
         * reelid is invalid.
         */
        ASSERT(0);
    }

    return position;
}



/**
 * Note that these are expected to come in order.
 */

void
SlotGuts::addPayline(uint8 paylineid, uint8 reelid, uint8 wheelposition)
{
    SlotPaylineReel *paylineReel = (SlotPaylineReel *) 0;
    SlotPaylinePosition *position;

    position = new SlotPaylinePosition();
    position->payline = paylineid;
    position->reelid = reelid;
    position->wheelposition = wheelposition;

    if ( reelid < m_paylines.size() )
    {
        paylineReel = m_paylines[reelid];
    }
    else
    {
        ASSERT( reelid == m_paylines.size() );

        paylineReel = new SlotPaylineReel();
        m_paylines.push_back(paylineReel);
    }

    paylineReel->push_back(position);

    ASSERT( paylineid == paylineReel->size() );
}

void
SlotGuts::addPayout(uint8 payoutTypeId, const char *payoutName, int32 payoutAmount)
{
    SlotPayout *payout;

    if ( !m_payTable )
    {
        m_payTable = new ISlotPayColumn();
    }

    payout = new SlotPayout();
    payout->payoutTypeId = payoutTypeId;
    payout->payoutName = new char[strlen(payoutName) + 1];
    strcpy(payout->payoutName, payoutName);
    payout->payoutAmount = payoutAmount;

    m_payTable->push_back(payout);
}


void
SlotGuts::clearPayTable()
{

    while ( m_payTable->size() > 0 )
    {
        SlotPayout *del = m_payTable->back();
        delete [] del->payoutName;
        delete del;
        m_payTable->pop_back();
    }

    delete m_payTable;
    m_payTable = NULL;
}

Info *
SlotGuts::getInfo()
{
    return m_info;
}

bool8	
SlotGuts::saveGame()
{
	int32 result = SlotsSQL::ExecFailed;

	if (tracker && m_info)
	{
		tracker->lock();
        result = ((SlotsSQL*)tracker)->saveGame(ServerOpts::casinoId, this);
		tracker->unlock();
	}

	return (bool8)(result == SlotsSQL::NoError);
}

uint8
SlotGuts::getWheelhouseCount()
{
    return m_vecWheelHouse.size();
}

bool8
SlotGuts::loadSavedGame()
{
	int32 result = SlotsSQL::ExecFailed;

	int32 gutsid = -1;
	int64 gamenum = -1;
	uint8 bonusgamenum = 0;
	bool8 malfunctioned = false;
	bool8 completed = false;
	bool8 hasSavedGame = false;
    int32 betperline = 0;
	
	SlotSpin* restoredSpin = new SlotSpin();

	if (tracker && m_info)
	{
		tracker->lock();
        result = ((SlotsSQL*)tracker)->getSavedGame(this, &gutsid, &gamenum,&bonusgamenum,&malfunctioned,&completed,&betperline);
		tracker->unlock();
	}
	if(result == SlotsSQL::NoError)
	{
		if(malfunctioned)
		{
			//MALFUNCTION -- NEED CODE
			/// @todo
			result = SlotsSQL::ExecFailed;
			Log::msgWarning("Malfunction is set in SavedGame: %I64d", gamenum);

            // FogBugz 753 - GChapman
            // Uncommented the line below.
			m_hardMalfunction = true;
		}
	}

	if(result == SlotsSQL::NoError && gamenum>0 && !completed && hasSavedGames())
	{
		//we have one that needs reading
		if(gutsid != getGutsId())
		{
			DEBUGMSG(("Saved game restored to a different guts."));
			///@todo need to figure out if that's okay or not.
			//It's okay for all of the first generation games (since no savedgame data is in guts)
		}
		
		//get the spin
		tracker->lock();
		result = ((SlotsSQL*)tracker)->loadGameDataIntoSpin(gamenum,this,restoredSpin);
		tracker->unlock();
	
		if(result == SlotsSQL::NoError)
		{
			//Set the bonus game type
			restoredSpin->setBonusGameType(this->m_subGame->getDataAt(0));

			m_spin = restoredSpin;
			restoredSpin = 0;

			//set the gamenum
			hasSavedGame = true;
			//Set the bonus game type

            m_info->betperline = getBetPerLine();
		}
	}

	delete restoredSpin;

	return (bool8)((result == SlotsSQL::NoError)&&hasSavedGames()&&hasSavedGame);

}

bool8	
SlotGuts::saveSpin()
{
	if ( shouldSaveGameData() )
	{
		int32 result = SlotsSQL::ExecFailed;

		if (tracker && m_info)
		{
			tracker->lock();
			result = ((SlotsSQL*)tracker)->saveSpins(ServerOpts::casinoId, this);
			tracker->unlock();
		}

		return (bool8)(result == SlotsSQL::NoError);
	}

	return true;
}


bool8
SlotGuts::saveBonusGames()
{
	int32 result = SlotsSQL::NoError;

    // kharmon.  04-05-07.  #19309.  Don't save bonus data for fun games...
	if ( shouldSaveGameData() )
	{
		// We need to have a spin to save bonus games.
		if ( m_spin )
		{
			if (tracker && m_info)
			{
				tracker->lock();
				result = ((SlotsSQL*)tracker)->saveBonusGames(this);
				tracker->unlock();
		    }
		}
	}

	return (bool8)(result == SlotsSQL::NoError);
}

bool8	
SlotGuts::completeGame()
{
	//sanity check
	if (!getGameCanComplete())
	{
		ASSERT(0);
	}

	int32 result = SlotsSQL::ExecFailed;

	if (tracker && m_info)
	{
		tracker->lock();
        result = ((SlotsSQL*)tracker)->completeGame(this);
		tracker->unlock();

        if ( result == true /*SlotsSQL::NoError*/ )
        {
            this->resetForNewGame();
        }
        else
        {
            /**
             * Tracker call failed.
             */
            ASSERT(0);
        }
	}

	return (bool8)(result == SlotsSQL::NoError);
}


bool8
SlotGuts::load(Info *info)
{
    bool8 rc = false;
    m_info = info;

    SlotsSQL *slotTracker = (SlotsSQL *) tracker;
    if ( tracker )
    {
		slotTracker->lock();

        if ( slotTracker->getGuts(this) == SlotsSQL::NoError )
		{
			rc = true;
		}

		slotTracker->unlock();
    }

    if ( rc )
    {
        rc = isValidGuts();
    }

    if ( rc )
    {
        allocateSubGameData();
        allocateFeatureData();
        loadSubGameData();
//        loadFeatureData();
    }
    else
    {
        /**
         * Guts is invalid.
         */
        DEBUGMSG(("Invalid guts detected for machid %d: gutsid: %d", m_machid, m_gutsid));
        ASSERT(0);
    }

#ifdef DEBUGSPIN
	if(m_bDebugSpinsMode)
	{
		initDebugFunctionMap();
	}
#endif	

    return rc;
}

bool8
SlotGuts::isValidGuts()
{
    /**
     * By default all guts are valid unless a list
     * is provided to restrict it.
     */
    bool8 rc = true;

    return rc;
}

		
bool8
SlotGuts::getGameCanComplete()
{
    return m_gameCanComplete;
}

bool8
SlotGuts::setGameCanComplete(bool8 canComplete)
{
	return (m_gameCanComplete = canComplete);
}

bool8
SlotGuts::isMalfunction()
{
    bool8 rc = m_hardMalfunction;

    return rc;
}

int32
SlotGuts::getRandomFromArray(int32* data, uint32 numElements)
{
	//This will be truly random if numElements is a power of two (it should be)
	//Although it won't matter too much if numElements is <<<< max_uint32

	uint32 randomValue = getrand();

	return data[randomValue % numElements];
}


uint8
SlotGuts::findPositionByWeight(uint8 gamemodeid, int64 weight, uint8 reelid)
{
    int32 position = 0;


    ISlotReel *reel = (*m_vecWheelHouse[gamemodeid])[reelid];
    /**
     * Starting at the end, look for the first weight
     * that is greater than the target weight.
     */
    for ( int i = reel->size() - 1; i >= 0; i-- )
    {
        if ( weight > getIcon(gamemodeid, reelid, i)->weight )
        {
            position = getIcon(gamemodeid, reelid, i)->position;
            break;
		}
    }

    return position;

}

void SlotGuts::preSpinClassifyResults( SlotSpin* spin )
{
#ifdef DEBUGSPIN
	if(m_bDebugSpinsMode)
	{
		m_bForceRandomJackpot = false;
		processDebugStops( spin );
	}
#endif
}

void
SlotGuts::postSpinClassifyResults( SlotSpin* spin )
{
    // By default, this method does nothing.
    // But, if a slot machine needs to alter the values
    // on the reel  after classifying the results
    // (as Super Diamond Mine does when it cascades the diamonds
    // on to other pay lines), then that can be done by overriding
    // this method in the derived class.
}

uint8
SlotGuts::getDenominationCount() const
{
    return m_vecDenominations.size();
}

void
SlotGuts::clearDenominations()
{
    while ( m_vecDenominations.size() > 0 )
    {
        delete m_vecDenominations.back();
        m_vecDenominations.pop_back();
    }
}

SlotGameDenomination*
SlotGuts::getDenomination(uint8 nIndex)
{
    if ( nIndex >= 0 && nIndex < getDenominationCount() )
    {
        return m_vecDenominations[nIndex];
    }
    else
    {
        return NULL;
    }
}

void
SlotGuts::addDenomination(uint16 machineSubId, uint16 creditValue, bool8 isDefault)
{
    SlotGameDenomination *pDenom = new SlotGameDenomination(machineSubId, creditValue, isDefault );
    m_vecDenominations.push_back(pDenom);
}

uint8
SlotGuts::getGameModeId() const
{
    return (uint8) m_subGame->getData(SGDIDX_GAME_MODE);
}

void
SlotGuts::setGameModeId( uint8 nGameModeId)
{
    m_subGame->update(SGDIDX_GAME_MODE,nGameModeId);
}

int32
SlotGuts::getFeatureGameCap() const
{
    return FEATURE_GAME_CAP;
}

void
SlotGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
{
    nGameModeMultiplier = 1;
    nNoteId = 255;
}

bool8
SlotGuts::getRandomProgressiveJackpotResult(uint8 lineCount)
{
    uint32 maxint = 4294967295;
	uint32 jackpotResult = maxint;
    uint32 basechance = getBaseChanceForRandomJackpot();
    bool8  bResult = false;

    if ( basechance > 0 )
    {
        basechance = basechance * 100 / ( getBetPerLine()*lineCount);
        uint32 boundary = ( maxint / basechance ) * basechance;
        do
	    {
		    jackpotResult = getrand();	
        } while( jackpotResult > boundary );

        bResult = ( jackpotResult < ( boundary / basechance ) );
    }

	return bResult;
}

int32
SlotGuts::getBaseChanceForRandomJackpot()
{
    return m_jackpotHitOdds;
}

uint32
SlotGuts::getReelMaxWeight(uint8 nGameModeId, uint8 nReelId)
{
    // Right now, this code only works if the wheel weights are even.
    // We will need to come up with another solution if we want wheels
    // to have different weights.

    uint32 maxweight = 0;
	uint32 reelsize = 0;

    ISlotReel *reel = (*m_vecWheelHouse[nGameModeId])[nReelId];
	reelsize = reel->size();

    uint32 baseweight = getIcon(nGameModeId, nReelId, 1 )->weight;
    maxweight = baseweight * reelsize;

    // If the wheels are evenly weighted, this should be true.
    ASSERT( (maxweight - baseweight) == getIcon(nGameModeId, nReelId, reel->size()-1 )->weight );

    return maxweight-1;
}

void
SlotGuts::storeWheelView(SlotSpin* pSpin)
{
	for(int row = 0; row < getRowCount(); row++)
	{
  		for(int reel = 0; reel<getReelCount(); reel++)
		{
			uint8 stop = relativeWheelPosition(pSpin->getGameModeId(), reel, pSpin->getStops()[reel], row);
			uint8 whichItem = getIcon(pSpin->getGameModeId(), reel, stop)->iconid;
            pSpin->addWheelViewIcon(reel,row,whichItem);
        } // reel
	} // row
}

uint8
SlotGuts::getRowCount()
{
    return 3;
}

int32
SlotGuts::getTotalWin()
{
    return m_subGame->getData(SGDIDX_TOTAL_WIN);
}

void
SlotGuts::setTotalWin(int32 totalWin)
{
    m_subGame->update(SGDIDX_TOTAL_WIN,totalWin);
}

void
SlotGuts::increaseTotalWin( int32 addWinnings )
{
    int32 currentWinnings = getTotalWin();

    setTotalWin( currentWinnings + addWinnings );
}

bool8
SlotGuts::getMaxPayoutAwarded() const
{
	if ( m_subGame->getData( SGDIDX_MAX_PAYOUT_AWARDED ) )
	{
		return true;
	}

	return false;
}

void
SlotGuts::setMaxPayoutAwarded( bool8 bMaxPayoutAwarded /*= true*/ )
{
	m_subGame->update( SGDIDX_MAX_PAYOUT_AWARDED, bMaxPayoutAwarded );
}

void SlotGuts::resetDataForNextSpin()
{
	setFeatureTriggered( false );

	setReelFeatureTriggerStep( 0, 0 );
	setReelFeatureTriggerStep( 1, 0 );
	setReelFeatureTriggerStep( 2, 0 );
	setReelFeatureTriggerStep( 3, 0 );
	setReelFeatureTriggerStep( 4, 0 );

	setReelAnticipation( 0, false );
	setReelAnticipation( 1, false );
	setReelAnticipation( 2, false );
	setReelAnticipation( 3, false );
	setReelAnticipation( 4, false );
}

bool SlotGuts::getFeatureTriggered() const
{
	if ( m_subGame->getData( SGIDX_FEATURE_TRIGGERED ) )
	{
		return true;
	}

	return false;
}

void SlotGuts::setFeatureTriggered( bool bTriggered )
{
	m_subGame->update( SGIDX_FEATURE_TRIGGERED, bTriggered );
}

int32 SlotGuts::getReelFeatureTriggerStep( int32 nReel ) const
{
	if ( nReel >= 0 && nReel < getReelCount() )
	{
		return m_subGame->getData( SGIDX_REEL_0_TRIGGER_STEP + nReel );
	}

	return -1;
}

void SlotGuts::setReelFeatureTriggerStep( int32 nReel, int32 nStep )
{
	if ( nReel >= 0 && nReel < getReelCount() )
	{
		m_subGame->update( SGIDX_REEL_0_TRIGGER_STEP + nReel, nStep );
	}
}

bool SlotGuts::getReelAnticipation( int32 nReel ) const
{
	if ( nReel >= 0 && nReel < getReelCount() )
	{
		if ( m_subGame->getData( SGIDX_REEL_0_ANTICIPATION + nReel ) )
		{
			return true;
		}
	}

	return false;
}

void SlotGuts::setReelAnticipation( int32 nReel, bool bAnticipation )
{
	if ( nReel >= 0 && nReel < getReelCount() )
	{
		m_subGame->update( SGIDX_REEL_0_ANTICIPATION + nReel, bAnticipation );
	}
}

int32
SlotGuts::getFreeSpins() const
{
    return m_subGame->getData(SGDIDX_FREE_SPINS_REMAINING);
}

void
SlotGuts::incrementFreeSpins(int32 numSpins /* = 1 */ )
{
    int32 numFreeSpinsRemaining = getFreeSpins();

	m_subGame->update(SGDIDX_FREE_SPINS_REMAINING,numFreeSpinsRemaining + numSpins);
}

void
SlotGuts::decrementFreeSpins(int32 numSpins /* = 1 */ )
{
    int32 numFreeSpinsRemaining = getFreeSpins();

    if ( numFreeSpinsRemaining >= numSpins )
    {
	    m_subGame->update(SGDIDX_FREE_SPINS_REMAINING,numFreeSpinsRemaining - numSpins);
    }
    else
    {
		removeAllFreeSpins();
        ASSERT(false);
    }
}

void SlotGuts::removeAllFreeSpins()
{
    m_subGame->update(SGDIDX_FREE_SPINS_REMAINING,0);
}

void
SlotGuts::setBetPerLine( int32 betperline )
{
    m_subGame->update(SGIDX_BET_PER_LINE,betperline);
}

int32
SlotGuts::getBetPerLine()
{
    return m_subGame->getData(SGIDX_BET_PER_LINE);
}

void
SlotGuts::setNumLinesBet( int32 numLinesBet )
{
    m_subGame->update(SGIDX_NUM_LINES_BET,numLinesBet);
}

int32
SlotGuts::getNumLinesBet()
{
    return m_subGame->getData(SGIDX_NUM_LINES_BET);
}

bool8
SlotGuts::warnOnClientDataWithoutSpin() const
{
	// default behavior is to enable warnings
	return true;
}

BonusGame*
SlotGuts::addBonusGame(uint8 bonusgametype,uint8 bonusgameindex,int32 basepayoutamount,int32 totalmultiplier,int32 totalbonuspayoutamount,int32 freespins,int64 overridegamenum,bool8 bSaved)
{
	int32 nPayout = (totalbonuspayoutamount/getBetPerLine());

	if ( getBetPerLine() > 0 )
	{
		// See if this bonus game puts us over the maximum win.  If it is equal to the maximum
		// win we still need to check to get rid of any additional free spins.
		if (!isThirdParty)
		{
			if ( ( getTotalWin() + nPayout ) >= getFeatureGameCap() )
			{
				nPayout = getFeatureGameCap() - getTotalWin();

				basepayoutamount = totalbonuspayoutamount = nPayout*getBetPerLine();
				totalmultiplier = 1;
				freespins = 0;

				// Set the max payout flag.
				setMaxPayoutAwarded();

  				// Get rid of the remaining free spins.
				removeAllFreeSpins();
			}
		}

		increaseTotalWin(nPayout);
	}

	SlotSpin* spin = getSlotSpin();
	return spin->addBonusGame(bonusgametype, bonusgameindex, basepayoutamount, totalmultiplier, totalbonuspayoutamount, freespins, overridegamenum);

}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::canSpin() const
///
/// @param   bool8    True/false indicating if the machine is in a spinnable state.
///
/// This is a framework method to determine if the machine is in a spinnable state.
////////////////////////////////////////////////////////////////////////////
bool8
SlotGuts::canSpin()
{
	bool8 bCanSpin = true;

	if ( isBonusGameInProgress() )
	{
		bCanSpin = false;
	}

	// Right now, you can't spin if a bonus game is in progress.
	return bCanSpin;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::isBonusGameInProgress() const
///
/// @param   bool8    True/False indicating if a bonus game is in progress.
///
/// This is a framework method to determine if a bonus game is in progress.
////////////////////////////////////////////////////////////////////////////
bool8
SlotGuts::isBonusGameInProgress()
{
	int32 nBonusGameState = getBonusGameState();

	// We assume that if the bonus game state is greater than zero, that a bonus game is in progress.
	return (bool8)( nBonusGameState > 0 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::getBonusGameState() const
///
/// @param   int32   The bonus game state.
///
/// This is a framework method to determine the bonus game state.
////////////////////////////////////////////////////////////////////////////
int32
SlotGuts::getBonusGameState() const
{
    int32 nRetVal = 0;
    
    if(m_subGame!=NULL) // kharmon.  07-20-07.  #19494.  Just in case...
    {
        nRetVal = m_subGame->getData(SGIDX_BONUS_GAME_STATE);
    }

    return nRetVal;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::setBonusGameState( int32 bonusGameState )
///
/// @param   bonusGameState   The bonus game state.
///
/// This is a framework method to set the bonus game state.
////////////////////////////////////////////////////////////////////////////
void
SlotGuts::setBonusGameState( int32 bonusGameState )
{
    m_subGame->update(SGIDX_BONUS_GAME_STATE,bonusGameState);
}




//Debug Spins Functionality

void SlotGuts::processDebugStops( SlotSpin *spin )
{
	if (!m_debugReelStops.empty())
	{
        if ( m_nDebugSpinIndex >= m_debugReelStops.size() )
        {
            m_nDebugSpinIndex = 0;
        }

		// Get a pointer to the stops array and cast
		// away the constness.  I know, I know, bad programmer!
		// Normally I would not do this, but, as this is just
		// a unit test/debug feature, I am ok with it.
		uint8* pStops = const_cast<uint8*>(spin->getStops());

		// Copy the stops from our array.
        if ( m_debugReelStops[m_nDebugSpinIndex][0] >= THIS_IS_A_FUNCTION )
        {
            int32 key = m_debugReelStops[m_nDebugSpinIndex][1];

            DebugStopFuncBase* func = m_mapDebugFunctions[key];

            if ( func && (
				((key >= BEGIN_BASE_MODIFY_NEXT_RESULT_FUNCTION_RANGE ) && (key <= END_BASE_MODIFY_NEXT_RESULT_FUNCTION_RANGE)) ||
				((key >= BEGIN_SUB_MODIFY_NEXT_RESULT_FUNCTION_RANGE ) && (key <= END_SUB_MODIFY_NEXT_RESULT_FUNCTION_RANGE)) ))
            {
				//we cannot have a modifier as the last spin, otherwise
				//we could have an infinite loop (case: 14577)
				if(m_nDebugSpinIndex != m_debugReelStops.size() - 1)
				{
					m_nDebugSpinIndex++;
					processDebugStops( spin );
					m_nDebugSpinIndex--;
				}
            }

            (*func)(spin );
        }
        else
        {
            for (uint8 n = 0; n < 5; n++ )
            {
                pStops[n] = m_debugReelStops[m_nDebugSpinIndex][n];
            }
        }

		m_nDebugSpinIndex++;
	}
}

void SlotGuts::initDebugFunctionMap()
{
	addDebugStopFunction( EVT_DEBUG_FUNCTION_FEATURE_EXIT,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_FeatureExit) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_LOSER_SPIN,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_Loser) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RANDOM_SPIN,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_RandomSpin) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RANDOM_JACKPOT,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_RandomJackpot) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RANDOM_JACKPOT_NEXT,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_RandomJackpot) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_RIGHT_TO_LEFT,
		new DebugStopFunc<SlotGuts>(this, &SlotGuts::DebugStops_RightToLeft) );
}

void SlotGuts::addDebugStopFunction( int32 key, DebugStopFuncBase* func )
{
    m_mapDebugFunctions.insert( make_pair( key,func ) );
}


////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::DebugStops_FeatureExit( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// This debug stop function gives loser spins until there are no
/// free games remaining. See setLoserStops()
////////////////////////////////////////////////////////////////////////////
void SlotGuts::DebugStops_FeatureExit(SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// Return a losing spin.
	Debug_SetLoserStops( pStops );

	// If there are still more free spins remaining...
	if ( getFreeSpins() > 1 )
	{
		// then decrement the spin index, which will cause this
		// function to be called again.
		m_nDebugSpinIndex--;
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// This debug stop function gives a set of losing stops.
/// Derived classed should override if all zeros does not create a loser.
////////////////////////////////////////////////////////////////////////////
void SlotGuts::DebugStops_Loser( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	Debug_SetLoserStops( pStops );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// This debug stop function gives derived class a chance to define a losing
/// stop.
/// Derived classed should override if all zeros does not create a loser.
////////////////////////////////////////////////////////////////////////////
void SlotGuts::Debug_SetLoserStops( uint8* pStops )
{
	memset( pStops, 0, 5 * sizeof( uint8 ) );
}

void SlotGuts::DebugStops_RandomSpin( SlotSpin *spin)
{
	return;
}

void SlotGuts::DebugStops_RandomJackpot( SlotSpin *spin)
{
	m_bForceRandomJackpot = true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::DebugStops_RightToLeft( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// This debug stop function takes a left to right result and returns the
//  equivalent right to left result if possible.
////////////////////////////////////////////////////////////////////////////
void SlotGuts::DebugStops_RightToLeft(SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	uint8 uRightReel = 0;
	uint8 uLeftReel = getReelCount() - 1;

	while ( uRightReel < uLeftReel )
	{
		uint8 uRightIconID = DebugStops_findStopIconID( uRightReel, pStops[uRightReel], 1 );
		uint8 uLeftIconID  = DebugStops_findStopIconID( uLeftReel,  pStops[uLeftReel],  1 );

		pStops[uRightReel] = DebugStops_findStopWithIconID( uRightReel, uLeftIconID,  1 );
		pStops[uLeftReel]  = DebugStops_findStopWithIconID( uLeftReel,  uRightIconID, 1 );

		++uRightReel;
		--uLeftReel;
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::DebugStops_findStopWithIconID( uint8 reelid, uint8 iconid, uint8 paylineid )
///
/// @param   reelid		The reel
/// @param   position	The stop position
/// @param   paylineid	The payline
///
/// This function finds the icon at the specified debug stop and payline.
////////////////////////////////////////////////////////////////////////////
uint8 SlotGuts::DebugStops_findStopIconID( uint8 reelid, uint8 position, uint8 paylineid )
{
    // kharmon. 10-12-05.  Make sure we take into account the game mode when we go look for
    // the icon id.
    int nGameModeId = getGameModeId();
	const SlotReel& reel = *getSlotReel(nGameModeId, reelid);
	int offset = getPaylinePosition(paylineid, reelid);
	uint8 iconid = reel[relativeWheelPosition(nGameModeId,reelid,position,offset)]->iconid;
	return iconid;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SlotGuts::DebugStops_findStopWithIconID( uint8 reelid, uint8 iconid, uint8 paylineid )
///
/// @param   reelid		The reel
/// @param   iconid		The icon
/// @param   paylineid	The payline
///
/// This function finds the first stop that will produce the
//  desired icon at the specified reel and payline.
////////////////////////////////////////////////////////////////////////////
uint8 SlotGuts::DebugStops_findStopWithIconID( uint8 reelid, uint8 iconid, uint8 paylineid )
{
    // kharmon. 10-12-05.  Make sure we take into account the game mode when we go look for
    // the icon position.
    int nGameModeId = getGameModeId();
	const SlotReel& reel = *getSlotReel(nGameModeId, reelid);
	uint8 position = 0;
	int offset = getPaylinePosition(paylineid, reelid);
	while( reel[relativeWheelPosition(nGameModeId,reelid,position,offset)]->iconid != iconid )
	{
		++position;
	}
	return position;
}
